Autosumme in ALV-Grid – Version 1
In einem Projekt wurde eine Anwendung programmiert, in der viele numerische Daten berechnet und ausgegeben wurden. Die Berechnungsergebnisse wurden auf mehrere Zellen verteilt. Um überprüfen zu können, ob die Verteilung richtig programmiert war, musste man die Summe über die verteilten Werte bilden und mit dem Ausgangswert vergleichen. Dies war immer relativ mühselig, da man entweder die Werte per Copy&Paste nach Excel kopieren musste (dann gab es aber Probleme bei negativen Zahlen…!) oder man musste die komplette Tabelle in Excel öffnen. Alles natürlich möglich, aber mir doch etwas zu umständlich.
Ich erinnerte mich an das Event delayed_changed_sel_callback, mit dem man eine Selektion im Grid verzögert auswerten konnte. Mit Hilfe der Tastenkombination STRG+Y kann man einzelne Zellen markieren. Diese Funktionen wollte ich nutzen, um die markierten Zellenwerte auszulesen und zu summieren.
Herausgekommen ist das unten stehende Programm. Der eigentliche Teil, die Summierung der Zellen, steht komplett in der Methode HANDLE_DELAYED_SELECTION.
Vorgehen
Event delayed_changed_sel_callback registrieren und einen Eventhandler in der Klasse zuordnen (Handle_Delayed_Selection).
Wie kommt man an die markierten Zellen heran? Dafür gibt es die Methode Get_Selected_Cells. Sie liefert eine Tabelle zurück, in der die markierten Feldnamen und der Zeilenindex stehen. Mittels READ TABLE und ASSIGN COMPONENT kann man also auf einen Zellwert zugreifen.
Nun muss noch mittels DESCRIBE FIELD geprüft werden, ob es sich um ein Feld mit einem numerischen Wert handelt. Wenn das der Fall ist, kann der Wert der Zelle aufsummiert werden.
Normale Zellen vs. Summenzeilen
Das Vorgehen bei einem ALV-Grid, in dem keine Summen oder Zwischensummen gebildet wurden, ist einfach und erfolgt nach dem oben genannten Schema. Eine Herausforderung sind jedoch die (Zwischen-) Summenstufen gewesen. Diese werden im ALV-Grid in eigenen – geschützten – Tabellen verwaltet:
Tabelle | Bedeutung | Bemerkung |
MT_CT00 | Summetabelle | Sie enthält in der Regel nur einen Eintrag. Ausnahme: Es sind in der Summierung der Feldwerte unterschiedliche Einheiten vorhanden. |
MT_CT01 | Zwischensummentabelle 1 | Erste Zwischensummenstufe |
MT_CTnn | Zwischensummentabelle n | Tabellen für Zwischensummenstufe nn |
MT_CT09 | Zwischensummentabelle 9 | 9 ist die höchste Stufe. Mehr Zwischensummen können nicht erstellt werden. |
Immerhin gibt es in diesem Fall eine Methode, mit der man sich die Zwischensummentabellen – bzw. eine Referenz auf diese – besorgen kann: Get_Subtotals. Im Feld ROW_ID-ROWTYPE der Zellen-Tabelle steht, ob es sich um die Totals-Tabelle handelt (1. Zeichen = T) oder eine Zwischensumme (1. Zeichen = S). Nicht gruppierte Zellen haben den Eintrag SPACE.
Eine Selektion auf ein Summenfeld liefert zum Beispiel diesen ROWTYPE: S 0101X0000000001. Der Vierstellige Code nach dem S sagt aus, um welche Hierarchiestufe es sich handelt (Stellen 1 und 2 des Codes). Die Stellen 3 und 4 des Codes sagen aus, in welcher Tabelle das markierte Feld steht. In diesem Fall ist es Level 1 der Hierarchie und Tabelle MT_CT01.
Anhand dieses Code kann man also herausfinden, in welcher Zwischensummentabelle nachgeschaut werden muss. Dies tue ich hier:
lv_index = ls_cell-row_id-rowtype+4(2). lv_tablename = 'LD_CT' && lv_index. ASSIGN (lv_tablename) TO <ref_data>.
Nachdem wir nun wissen, in welcher Tabelle wir nachsehen müssen um den markierten Zellwert zu finden, müssen wir nun noch den richtigen Index ermitteln. Dieser wird leider nicht mitgegeben, sondern muss aus der Tabelle GROUPLEVELS, die über Get_Subtotals geliefert wird, ermittelt werden:
READ TABLE lt_grouplevels INTO ls_grouplevel INDEX ls_cell-row_id-index. IF sy-subrc = 0. ls_cell-row_id-index = ls_grouplevel-cindx_from. ENDIF.
Achtung! Das Programm funktioniert nur, wenn es sich um reine (Zwischen-) Summen handelt! Zwischensummen, die aus mehreren Zeilen bestehen weil sich die zugehörige Einheit unterscheidet, können (noch) nicht erkannt werden. Hier muss ich noch etwas forschen…
Zwischensummen bilden
Ein kurzer Hinweis, wie man im ALV-Grid Zwischensummen bildet:
Wähle als erstes mindestens eine Spalte über die du dann mit Hilfe des Summenicons eine Summe bildest. Danach kannst du weitere Spalten markieren und mit dem Zwischensummenicon die Spalten definieren, über die zusätzlich eine Zwischensumme erstellt werden soll.
Aufrisssummenstufe
Über die Aufrisssummenstufe kannst du einfach festlegen, dass nur Zwischensummenzeilen einer bestimmten Hierarchie angezeigt werden sollen:
Auswahl im Menü:
Auswahl der Hierarchieebene:
Anzeige der gewählten Zwischensummen:
Code
REPORT zz_alv_autosumme. PARAMETERS p_total TYPE p DECIMALS 2. CLASS lcl_main DEFINITION. PUBLIC SECTION. METHODS start. PROTECTED SECTION. DATA mr_grid TYPE REF TO cl_gui_alv_grid. DATA mt_data TYPE STANDARD TABLE OF spfli. DATA mv_data_table TYPE tabname VALUE 'SPFLI'. DATA mr_dock TYPE REF TO cl_gui_docking_container. METHODS create_docker. METHODS create_grid. METHODS handle_delayed_selection FOR EVENT delayed_changed_sel_callback OF cl_gui_alv_grid IMPORTING sender. METHODS register_events. METHODS select_data. ENDCLASS. CLASS lcl_main IMPLEMENTATION. METHOD start. select_data( ). create_docker( ). create_grid( ). register_events( ). ENDMETHOD. METHOD create_docker. "Create Docking container at bottom CREATE OBJECT mr_dock EXPORTING side = cl_gui_docking_container=>dock_at_bottom ratio = 90 no_autodef_progid_dynnr = abap_false. ENDMETHOD. METHOD create_grid. "Create ALV-Grid CREATE OBJECT mr_grid EXPORTING i_appl_events = abap_true i_parent = mr_dock. "and display data mr_grid->set_table_for_first_display( EXPORTING i_structure_name = mv_data_table CHANGING it_outtab = mt_data ). "Set focus on grid so user can directly scroll and select cells via CTRL+Y cl_gui_container=>set_focus( mr_grid ). ENDMETHOD. METHOD handle_delayed_selection. "Local data DATA lt_cells TYPE lvc_t_cell. DATA ls_cell LIKE LINE OF lt_cells. DATA lv_total TYPE p DECIMALS 2. DATA lv_val_type TYPE c. DATA lv_index TYPE n LENGTH 2. DATA lv_tablename TYPE string. DATA lt_grouplevels TYPE lvc_t_grpl. DATA ls_grouplevel LIKE LINE OF lt_grouplevels. FIELD-SYMBOLS <ref_data> TYPE REF TO data. FIELD-SYMBOLS <table> TYPE table. FIELD-SYMBOLS <warea> TYPE any. FIELD-SYMBOLS <val> TYPE any. "data references to sub totals tables DATA ld_ct00 TYPE REF TO data. DATA ld_ct01 TYPE REF TO data. DATA ld_ct02 TYPE REF TO data. DATA ld_ct03 TYPE REF TO data. DATA ld_ct04 TYPE REF TO data. DATA ld_ct05 TYPE REF TO data. DATA ld_ct06 TYPE REF TO data. DATA ld_ct07 TYPE REF TO data. DATA ld_ct08 TYPE REF TO data. DATA ld_ct09 TYPE REF TO data. "get selected cells (selection via CTRL + Y) sender->get_selected_cells( IMPORTING et_cell = lt_cells ). "If there is only one cell selected, we do not need to sum that... CHECK lines( lt_cells ) > 1. "Read all cell values LOOP AT lt_cells INTO ls_cell. "in case of rowtype (normal cell, total or subtotal) assign correct data table CASE ls_cell-row_id-rowtype(1). "Total sum of all WHEN 'T'. sender->get_subtotals( IMPORTING ep_collect00 = ld_ct00 ). ASSIGN ld_ct00 TO <ref_data>. ls_cell-row_id-index = 1. "assign specified data table ASSIGN <ref_data>->* TO <table>. "subtotals WHEN 'S'. sender->get_subtotals( IMPORTING ep_collect01 = ld_ct01 ep_collect02 = ld_ct02 ep_collect03 = ld_ct03 ep_collect04 = ld_ct04 ep_collect05 = ld_ct05 ep_collect06 = ld_ct06 ep_collect07 = ld_ct07 ep_collect08 = ld_ct08 ep_collect09 = ld_ct09 et_grouplevels = lt_grouplevels ). lv_index = ls_cell-row_id-rowtype+4(2). lv_tablename = 'LD_CT' && lv_index. ASSIGN (lv_tablename) TO <ref_data>. READ TABLE lt_grouplevels INTO ls_grouplevel INDEX ls_cell-row_id-index. IF sy-subrc = 0. ls_cell-row_id-index = ls_grouplevel-cindx_from. ENDIF. "assign specified data table ASSIGN <ref_data>->* TO <table>. "Normal cell value WHEN space. ASSIGN mt_data TO <table>. ENDCASE. "Only read table line when index changes AT NEW row_id. READ TABLE <table> ASSIGNING <warea> INDEX ls_cell-row_id-index. ENDAT. "Assign selected fieldname of workarea ASSIGN COMPONENT ls_cell-col_id OF STRUCTURE <warea> TO <val>. IF sy-subrc = 0. "check correct type of field: Only numeric fields will be taken DESCRIBE FIELD <val> TYPE lv_val_type. CASE lv_val_type. WHEN 'P' "Packed OR 'N' "Numchar OR 'b' "Integer OR 'a' "decfloat OR 'e' "decfloat OR 'F'. "Float? "add cell value to total ADD <val> TO lv_total. ENDCASE. ENDIF. ENDLOOP. IF lv_total IS NOT INITIAL. "There were numeric fields selected and therefor we have a total to show: MESSAGE s000(oo) WITH 'TOTAL:' space lv_total. "Parameterfeld ebenfalls füllen p_total = lv_total. ENDIF. ENDMETHOD. METHOD register_events. "Set handler SET HANDLER handle_delayed_selection FOR mr_grid. "register event for delayed selection mr_grid->register_delayed_event( mr_grid->mc_evt_delayed_change_select ). ENDMETHOD. METHOD select_data. "Select data SELECT * FROM (mv_data_table) INTO TABLE mt_data UP TO 100 ROWS. ENDMETHOD. ENDCLASS. INITIALIZATION. DATA(gr_main) = NEW lcl_main( ). gr_main->start( ).
- Interview mit Björn Schulz (Software-Heroes.com) - 3. September 2024
- Daten aus ALV ermitteln - 3. September 2024
- So lange es den SAPGUI noch gibt… - 27. Juni 2024